package io.quarkus.resteasy.reactive.server.test.providers;

import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;

import jakarta.ws.rs.core.HttpHeaders;

import org.hamcrest.Matchers;
import org.jboss.resteasy.reactive.FilePart;
import org.jboss.resteasy.reactive.PathPart;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.condition.EnabledIfSystemProperty;
import org.junit.jupiter.api.extension.RegisterExtension;

import io.quarkus.test.QuarkusUnitTest;
import io.quarkus.test.common.http.TestHTTPResource;
import io.restassured.RestAssured;

public class FileTestCase {

    private static final String FILE = "src/test/resources/lorem.txt";

    @TestHTTPResource
    URI uri;

    @RegisterExtension
    static final QuarkusUnitTest config = new QuarkusUnitTest()
            .withApplicationRoot((jar) -> jar
                    .addClasses(FileResource.class, WithWriterInterceptor.class, WriterInterceptor.class));

    @Test
    public void testFiles() throws Exception {
        String content = Files.readString(Path.of(FILE));
        String contentLength = String.valueOf(content.length());
        RestAssured.get("/providers/file/file")
                .then()
                .statusCode(200)
                .header(HttpHeaders.CONTENT_LENGTH, contentLength)
                .body(Matchers.equalTo(content));
        RestAssured.given().header("Range", "bytes=0-9").get("/providers/file/file")
                .then()
                .statusCode(206)
                .header(HttpHeaders.CONTENT_LENGTH, "10")
                .header("Content-Range", "bytes 0-9/" + contentLength)
                .body(Matchers.equalTo(content.substring(0, 10)));
        RestAssured.given().header("Range", "bytes=10-19").get("/providers/file/file")
                .then()
                .statusCode(206)
                .header(HttpHeaders.CONTENT_LENGTH, "10")
                .header("Content-Range", "bytes 10-19/" + contentLength)
                .body(Matchers.equalTo(content.substring(10, 20)));
        RestAssured.given().header("Range", "bytes=10-").get("/providers/file/file")
                .then()
                .statusCode(206)
                .header(HttpHeaders.CONTENT_LENGTH, String.valueOf(content.length() - 10))
                .header("Content-Range", "bytes 10-" + (content.length() - 1) + "/" + contentLength)
                .body(Matchers.equalTo(content.substring(10)));
        RestAssured.given().header("Range", "bytes=-10").get("/providers/file/file")
                .then()
                .statusCode(206)
                .header(HttpHeaders.CONTENT_LENGTH, "10")
                .header("Content-Range",
                        "bytes " + (content.length() - 10) + "-" + (content.length() - 1) + "/" + contentLength)
                .body(Matchers.equalTo(content.substring((content.length() - 10))));
        RestAssured.given().header("Range", "bytes=" + (content.length() + 1) + "-").get("/providers/file/file")
                .then()
                .statusCode(200)
                .header(HttpHeaders.CONTENT_LENGTH, contentLength)
                .body(Matchers.equalTo(content));
        RestAssured.given().header("Range", "bytes=0-1, 3-4").get("/providers/file/file")
                .then()
                .statusCode(200)
                .header(HttpHeaders.CONTENT_LENGTH, contentLength)
                .body(Matchers.equalTo(content));
        RestAssured.get("/providers/file/file-partial")
                .then()
                .statusCode(200)
                .header(HttpHeaders.CONTENT_LENGTH, "10")
                .body(Matchers.equalTo(content.substring(20, 30)));
        RestAssured.get("/providers/file/path")
                .then()
                .statusCode(200)
                .header(HttpHeaders.CONTENT_LENGTH, contentLength)
                .body(Matchers.equalTo(content));
        RestAssured.get("/providers/file/path-partial")
                .then()
                .statusCode(200)
                .header(HttpHeaders.CONTENT_LENGTH, "10")
                .body(Matchers.equalTo(content.substring(20, 30)));
        RestAssured.get("/providers/file/async-file")
                .then()
                .header(HttpHeaders.CONTENT_LENGTH, Matchers.nullValue())
                .statusCode(200)
                .body(Matchers.equalTo(content));
        RestAssured.get("/providers/file/rest-response-async-file")
                .then()
                .header("foo", "bar")
                .statusCode(200)
                .body(Matchers.equalTo(content));
        RestAssured.get("/providers/file/mutiny-async-file")
                .then()
                .header(HttpHeaders.CONTENT_LENGTH, Matchers.nullValue())
                .statusCode(200)
                .body(Matchers.equalTo(content));
        RestAssured.get("/providers/file/async-file-partial")
                .then()
                .statusCode(200)
                .header(HttpHeaders.CONTENT_LENGTH, "10")
                .body(Matchers.equalTo(content.substring(20, 30)));
    }

    @Test
    public void testChecks() throws IOException {
        // creation-time checks
        Path path = Paths.get(FILE);
        // works
        new PathPart(path, 10, 10);
        new PathPart(path, 0, Files.size(path));
        // fails
        try {
            new PathPart(path, -1, 10);
            Assertions.fail();
        } catch (IllegalArgumentException x) {
        }
        try {
            new PathPart(path, 0, -1);
            Assertions.fail();
        } catch (IllegalArgumentException x) {
        }
        try {
            new PathPart(path, 0, 1000);
            Assertions.fail();
        } catch (IllegalArgumentException x) {
        }
        try {
            new PathPart(path, 250, 250);
            Assertions.fail();
        } catch (IllegalArgumentException x) {
        }

        File file = new File(FILE);
        // works
        new FilePart(file, 10, 10);
        new FilePart(file, 0, file.length());
        // fails
        try {
            new FilePart(file, -1, 10);
            Assertions.fail();
        } catch (IllegalArgumentException x) {
        }
        try {
            new FilePart(file, 0, -1);
            Assertions.fail();
        } catch (IllegalArgumentException x) {
        }
        try {
            new FilePart(file, 0, 1000);
            Assertions.fail();
        } catch (IllegalArgumentException x) {
        }
        try {
            new FilePart(file, 250, 250);
            Assertions.fail();
        } catch (IllegalArgumentException x) {
        }
    }

    @EnabledIfSystemProperty(named = "test-resteasy-reactive-large-files", matches = "true")
    @Test
    public void testWithLargeFile() {
        RestAssured.given()
                .get("/providers/file/large-path-rest-response")
                .then()
                .statusCode(200)
                .contentType("application/octet-stream")
                .header(HttpHeaders.CONTENT_DISPOSITION, "large-file")
                .header(HttpHeaders.CONTENT_LENGTH, "1073741824");
    }
}
